package com.merry.annoreflex;

import java.lang.reflect.*;
import java.util.ArrayList;

/**
 * @author zouyb
 */
public class Reflex {
    public static void main(String[] args) throws Exception {
        Reflex reflex = new Reflex();
//        reflex.useReflex();
//        reflex.MethodReflex();
        reflex.ThisClassAnno();
    }

    /**
     * 0.只用反射获取泛型和对值的转换，以MyEntity为例
     * 实现的功能：根据反射获取这个类中所有变量的值，并且调用它们的方法，比如对象类型为String的，调用subString方法，对象类型为List的调用size或者add方法。
     */
    public void useReflex() throws IllegalAccessException, ClassNotFoundException {
        MyEntity myEntity = new MyEntity();
        Field[] declaredFields = MyEntity.class.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            Object o = field.get(myEntity);
            Type genericType = field.getGenericType();
            //转换类型
            /**
             * 方式1：使用class.cast转换，但是缺点是需要确定这个class是什么泛型的对象，但是我都知道了就不需要这样转换了。
             */
//            Class<String> aClass1 = (Class<String>) Class.forName(genericType.getTypeName());
//            String cast = aClass1.cast(o);
            /**
             * 方式2：采用instanceof的方式。缺点是需要写所有的类型，麻烦。
             */
            if(o instanceof String){
                String s = (String) o;
            }
            //获取泛型，这个方法当字段为泛型或者不是泛型时有两种截然不同的结果。这个才表示当前字段有泛型.
            if(genericType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
                Class<?> aClass = Class.forName(actualTypeArguments[0].getTypeName());
                System.out.println(aClass.getName());
            }
        }
    }

    //1.字段反射。
    public void FieldReflex() throws IllegalAccessException {
        MyEntity myEntity = new MyEntity();
        Class<MyEntity> clazz = MyEntity.class;
        Field[] fields = clazz.getDeclaredFields();
        //拿到当前类定义的所有字段之后。
        for (Field field : fields) {
            //设置他的可见性。
            field.setAccessible(true);
            //获取字段的名字
            String name = field.getName();
            //获取字段的类型
            Class type = field.getType();
            //获取当前字段的值。
            Object o = field.get(myEntity);
            //获取字段上的注解。
            field.getAnnotation(MyAnnotation.class);
        }
    }

    //2.方法反射。
    public void MethodReflex() throws IllegalAccessException, InstantiationException, InvocationTargetException {
        MyEntity myEntity = new MyEntity();
        Class<MyEntity> clazz = MyEntity.class;
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            ArrayList paramValue = new ArrayList();
            for (Class<?> parameterType : parameterTypes) {
                //如果获取到的类是一个接口，或者抽象类，那么调用这个方法会出错。
                paramValue.add(parameterType.newInstance());
            }
            Object invoke;
            invoke = method.invoke(myEntity, paramValue.toArray());
            if(!(invoke instanceof Void)){
                System.out.println(invoke);
            }
        }
    }

    //3.构造器反射。
    public void ConstructorReflex(){
        //使用不多忽略。
    }

    //4.父类反射。
    public void SuperClassReflex(){
        //直接使用class可以获取子类和父类的class对象。
    }

    //5.接口反射
    public void InterfaceReflex(){
        //直接使用class可以获取接口对象。
    }

    /**
     * 6.该类的注解获取。
     *  在字段反射或者方法反射中我们可以看出来，如果单纯的使用反射，那么程序将会很难的实现一些公共的方法，因为程序无法在运行的时候知道用户想要干什么。
     *  下面使用注解+反射的方式来尝试一下。
     */
    public void ThisClassAnno() throws IllegalAccessException {
        MyEntity myEntity = new MyEntity();
        Class<MyEntity> clazz = MyEntity.class;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //这个时候我们可以通过注解判断用户想要干什么的行为。
            MyAnnotation1 annotation = field.getAnnotation(MyAnnotation1.class);
            //当前字段有这个注解。
            if(annotation!= null){
                String s = annotation.newName();
                //给这个属性赋值。
                if(s != null && s != ""){
                    field.setAccessible(true);
                    field.set(myEntity,s);
                    System.out.println(field.get(myEntity));
                }
            }
        }
    }

}
